package com.orange.wwwapi.security.provider;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.orange.core.pojo.entity.user.User;
import com.orange.core.pojo.enums.EStatus;
import com.orange.core.security.detail.UserDetail;
import com.orange.core.security.token.WxAuthenticationToken;
import com.orange.core.service.user.UserService;
import com.orange.core.util.RequestUtil;
import com.orange.core.util.TokenUtil;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

@Slf4j
@RequiredArgsConstructor
public class WxAuthenticationProvider implements AuthenticationProvider {

    private final WxMaService wxMaService;
    private final UserDetailsService userDetailsService;
    private final UserService userService;

    protected UserDetail retrieveUser(String openId, String unionid) throws AuthenticationException {
        UserDetail userDetail;
        try {
            userDetail = (UserDetail) userDetailsService.loadUserByUsername(openId);
            if (userDetail == null) {
                int userId = userService.register(openId, unionid, null);
                User user = userService.getById(userId);
                userDetail = new UserDetail(user.getId(), user.getNickname(), user.getStatus() == EStatus.NORMAL.getValue())
                        .setUser(user).setToken(TokenUtil.newToken());
            }
        } catch (UsernameNotFoundException e) {
            log.error("查找账号失败");
            throw new UsernameNotFoundException("查找账号失败", e);
        }
        return userDetail;
    }

    protected void additionalAuthenticationChecks(UserDetail userDetail) {
        userDetail.authenticationCheck();
    }

    protected Authentication createSuccessAuthentication(UserDetails userDetails) {
        return new WxAuthenticationToken(userDetails, userDetails.getAuthorities());
    }

    @SneakyThrows
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        WxMaJscode2SessionResult sessionResult = wxMaService.jsCode2SessionInfo(authentication.getName());
        RequestUtil.getRequest().setAttribute("wxUserInfo", sessionResult);
        UserDetail userDetail = retrieveUser(sessionResult.getOpenid(), sessionResult.getUnionid());
        additionalAuthenticationChecks(userDetail);
        return createSuccessAuthentication(userDetail);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return WxAuthenticationToken.class.isAssignableFrom(authentication);
    }
}
